package tech.waterism.service;

import cn.hutool.core.date.DateUtil;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import tech.waterism.dto.*;
import tech.waterism.entity.DPower;
import tech.waterism.entity.FCurveHc;
import tech.waterism.utils.ConvertZC;

import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 水位耗水率关系曲线表表(FCurveHc)表服务实现类
 */
@Service
@AllArgsConstructor
public class FCurveHcService {

    public FCurveHcDto getcurveHc(FCurveHc dto) {
        FCurveHcDto result = new FCurveHcDto();
        if (dto.getLnnm() != null) {
            List<FCurveHc> curveList = baseMapper.selectList(
                    new LambdaQueryWrapper<FCurveHc>().eq(FCurveHc::getStcd, dto.getStcd())
                            .eq(FCurveHc::getLnnm, dto.getLnnm()).orderByAsc(FCurveHc::getNorow));
            if (curveList.size() > 0) {
                result.setLnnm(dto.getLnnm());
                result.setCurveHc(curveList);
                return result;
            }
        }
        List<FCurveHc> curveList = baseMapper.selectList(
                new LambdaQueryWrapper<FCurveHc>().eq(FCurveHc::getStcd, dto.getStcd()).orderByAsc(FCurveHc::getNorow));
        if (curveList.size() == 0) {
            return null;
        }
        Map<String, List<FCurveHc>> curveMap = curveList.stream().collect(Collectors.groupingBy(FCurveHc::getLnnm));
        if (curveMap.entrySet().size() == 1) {
            result.setLnnm(curveList.get(0).getLnnm());
            result.setCurveHc(curveList);
            return result;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.set(1900, Calendar.JANUARY, 2, 0, 0, 0);// 1900-01-02 00:00:00
        Date tempDate = calendar.getTime();
        Map<String, List<FCurveHc>> newCurveMap = new HashMap<>();
        for (Map.Entry<String, List<FCurveHc>> entry : curveMap.entrySet()) {
            if (DateUtil.isSameDay(tempDate, entry.getValue().get(0).getCreationDate())) {
                newCurveMap.put(entry.getKey(), entry.getValue());
            } else if (tempDate.before(entry.getValue().get(0).getCreationDate())) {
                newCurveMap.clear();
                tempDate = entry.getValue().get(0).getCreationDate();
                newCurveMap.put(entry.getKey(), entry.getValue());
            }
        }
        String tempKey = "";
        Set<String> tempKeySet = new HashSet<>();
        if (newCurveMap.size() > 1) {
            int tempLength = 0;
            for (Map.Entry<String, List<FCurveHc>> entry : newCurveMap.entrySet()) {
                if (entry.getValue().size() > tempLength) {
                    tempKeySet.clear();
                    tempLength = entry.getValue().size();
                    tempKey = entry.getKey();
                    tempKeySet.add(entry.getKey());
                } else if (entry.getValue().size() == tempLength) {
                    tempKeySet.add(entry.getKey());
                }
            }
            if (tempKeySet.size() == 1) {
                result.setLnnm(tempKey);
                result.setCurveHc(newCurveMap.get(tempKey));
                return result;
            }
            tempLength = 0;
            for (String key : newCurveMap.keySet()) {
                if (tempLength < key.length()) {
                    tempLength = key.length();
                    tempKey = key;
                }
            }
            result.setLnnm(tempKey);
            result.setCurveHc(newCurveMap.get(tempKey));
            return result;
        } else if (newCurveMap.size() == 1) {
            for (String s : newCurveMap.keySet()) {
                result.setLnnm(s);
                result.setCurveHc(newCurveMap.get(s));
                return result;
            }
        }
        return null;
    }

    public void calculatePower(FPIndexDto fpIndex, List<FCurveHc> curveHc, Date fymdh) {
        DPower dPower = dPowerMapper.selectOne(new LambdaQueryWrapper<DPower>().eq(DPower::getStcd, fpIndex.getStcd()));
        double maxQPower = dPower == null ? Double.MAX_VALUE : dPower.getQPowerMax();
        double maxNPower = dPower == null ? Double.MAX_VALUE : dPower.getNPowerMax();
        AtomicReference<Double> ePower = new AtomicReference<>((double) 0);
        Integer clen = fpIndex.getClen();
        String unt = fpIndex.getUnt();
        fpIndex.getData().stream()
                .filter(datum -> datum.getDt().after(fymdh) && datum.getRz() != null && datum.getQOut() != null)
                .forEach(datum -> {
                    Double c = ConvertZC.getCfromZ(datum.getRz(), curveHc);
                    if (c < 0) {
                        c = 0.0001;
                    }
                    //  出流应当小于最大发电流量
                    Double qPower = Math.min(datum.getQOut(), maxQPower);
                    datum.setQPower(qPower);
                    double nPowerTemp = qPower * 3600 / 1000 / c;
                    // 出力应当小于最大出力
                    Double nPower = Math.min(nPowerTemp, maxNPower);
                    datum.setNPower(nPower);
                    ePower.updateAndGet(v -> v + getEPowerWithTime(nPower, clen, unt));
                });
        fpIndex.setEPower(ePower.get());
    }

    private Double getEPowerWithTime(Double nPower, Integer clen, String unt) {
        if (unt == null)
            unt = "H";
        switch (unt) {
            case "H":
                return nPower * clen * 1000.0;
            case "M":
                return nPower * clen * 1000.0 / 60;
            default:
                return nPower * clen * 1000.0;
        }
    }
}
